import tensorflow as tf
import os

def fifoqueue():
    # 创建队列指定队列的元素
    queue = tf.FIFOQueue(3,tf.float32)
    # 向队列中添加元素
    en_many = queue.enqueue_many([[0.1,0.2,0.3],])
    # 定义一个出列的操作
    deq_op = queue.dequeue()
    # 对于出列的对象 +1
    # 实现了运算符的重载，如果是加号可以将1转换成tensor类型，并且调用add
    incre_op = deq_op + 1
    # 让 +1的对象在重新入列
    enq_op = queue.enqueue(incre_op)
    # 必须在会话中运行op对象
    # 以下的操作都是在主线程中完成的都是同步操作
    with tf.Session() as sess:
        # 运行添加元素的op（0.1,0.2,0.3)
        sess.run(en_many)

        # 完成值的处理操作
        for i in range(3):
            sess.run(enq_op)

        # 将队列的数据取出，将数据交给模型开始训练
        for i in range(queue.size().eval()):
            ret = sess.run(deq_op)
            print(ret)

def async_opration():
    '''
    通过队列管理器和线程协调器实现变量+1
    :return: None
    '''
    # 定义一个队列 容量1000，类型tf.float32
    queue = tf.FIFOQueue(1000,tf.float32)
    # 完成一个自增的操作，并且入列操作
    var = tf.Variable(0.0)
    # assign_add操作和enq_op不是同步执行，assign_add操作有可能执行很多次才会执行enq_op操作
    incre_op = tf.assign_add(var,tf.constant(1.0))
    # 入列
    enq_op = queue.enqueue(incre_op)
    # 出列
    deq_op = queue.dequeue()

    # 定义队列管理器
    qr = tf.train.QueueRunner(queue=queue,enqueue_ops=[enq_op]*2)

    init_op = tf.global_variables_initializer()
    # 通过with上下文创建的会话自动关闭，主线程已经执行完毕
    # 子线程会自动停止吗？子线程并不会退出 而是一种挂起的状态
    with tf.Session() as sess:
        sess.run(init_op)
        # 创建线程协调器
        coord = tf.train.Coordinator()
        # 通过队列管理器来创建需要执行的入列的线程
        # start为true 表示创建的线程 需要开启，enqueue的操作已经开始执行，并且是两个线程在执行
        threads = qr.create_threads(sess=sess,coord=coord,start=True)
        # 入列的操作在另外一个线程
        for i in range(1000):
            # 主线程deq出列
            ret = sess.run(deq_op)
            print(ret)
        # 主线程的任务执行结束
        # 应该请求结束子线程
        coord.request_stop()
        # coord.should_stop()
        # 加上线程同步
        coord.join(threads=threads)

    return None

def csv_reader():
    # 获取./data/csvdata/ 路径所有的文件
    file_names = os.listdir('./data/csvdata')
    file_names = [os.path.join('./data/csvdata/',file_name) for file_name in file_names]

    print(file_names)
    # 1.通过文件名创建文件队列 file_queue
    file_queue = tf.train.string_input_producer(file_names)

    # 2.创建文件读取器 reader按行读取
    reader = tf.TextLineReader()

    # 3.通过reader对象调用read reader.read(file_queue)
    # 返回的结果是 key value value指的是某一个文件的一行
    key,value = reader.read(file_queue)
    print(key,value)
    # 4.对value 进行decode操作
    col1,col2 = tf.decode_csv(value,record_defaults=[['null'],['null']],field_delim=',')

    # 5.建立管道读的批处理
    col1_batch,col2_batch = tf.train.batch(tensors=[col1,col2],batch_size=100,num_threads=2,capacity=100)

    with tf.Session() as sess:
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess,coord=coord)

        ret = sess.run([col1_batch,col2_batch])
        print(ret)
        # 主线程的任务执行完毕之后，应该请求关闭线程
        coord.request_stop()
        coord.join(threads)


def pic_reader():
    file_names = os.listdir('./data/dog/')
    file_names = [os.path.join('./data/dog/',file_name) for file_name in file_names]

    # 创建文件队列
    file_queue = tf.train.string_input_producer(file_names)
    # 创建读取器
    reader = tf.WholeFileReader()
    # key是文件名，value图片的数组数据
    key,value = reader.read(file_queue)
    # 通过解码的方式获取value的信息
    image = tf.image.decode_jpeg(value)
    # 在进行图片的批处理之前 需要讲图片的形状修改为一样的【200,200，？】
    resize_image = tf.image.resize_images(image,size=[200,200])
    # 设置图片的管道
    resize_image.set_shape([200,200,3])
    print(resize_image)
    # 要去进行批处理的时候还需要知道图片的通道数
    image_batch = tf.train.batch(tensors=[resize_image],batch_size=100,num_threads=2,capacity=100)

    print(image_batch)
    with tf.Session() as sess:
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess,coord=coord)
        ret = sess.run(image_batch)
        print(ret)

        coord.request_stop()
        coord.join(threads)

class Cifar(object):
    '''
    读取二进制文件的演示，将二进制文件得到的数据存储到TFRecords 并且读取TFRecords数据
    '''
    def __init__(self):
        self.height = 32
        self.width = 32
        self.channels = 3 # 彩色的图片
        self.label_bytes = 1
        self.image_bytes = self.height * self.width * self.channels
        # 每次需要读取的字节大小
        self.bytes = self.label_bytes + self.image_bytes

    def read_and_decode(self,file_names):
        '''
        读取并且解码二进制的图片
        :param file_names:
        :return: 将批处理的图片和标签返回
        '''
        # 构建文件队列 通过文件名的列表
        file_queue = tf.train.string_input_producer(file_names)
        # 创建文件读取器 并且指定每次读取的字节大小为 self.bytes
        reader = tf.FixedLengthRecordReader(self.bytes)
        # 读取二进制文件数据的uint8 一个字节 3073 = 1 + 3072
        key,value = reader.read(file_queue)
        # 完成对于二进制数据的解码操作
        label_image = tf.decode_raw(value,tf.uint8)
        # 在decode iamge 之前需要讲读取的3073个字节分割成1 和3072
        # 截取的起始位置和长度需要通过一个一阶的张量表示
        # 将self.bytes切分为self.label_bytes 和self.image_bytes
        label = tf.cast(tf.slice(label_image,[0],[self.label_bytes]),tf.int32)

        iamge = tf.cast(tf.slice(label_image,[self.label_bytes],[self.image_bytes]),tf.int32)
        # 图片的字节为3072，需要将这个3072个字节的形状重新设置【32*32*3】
        reshape_image = tf.reshape(iamge,shape=[32,32,3])
        # 由于读取的图片的形状都是一样的，不需要做resize处理，直接进行批处理
        image_batch,label_batch = tf.train.batch([reshape_image,label],batch_size=100,num_threads=2,capacity=100)

        return image_batch,label_batch

    def save_to_tfrecords(self,image_batch,label_batch):
        '''
        将读取的图片数据存储为tfrecord格式文件
        :return:
        '''

        # 1.创建存储器
        writer = tf.python_io.TFRecordWriter('./data/cifar.tfrecords')
        # 2.构建存储的样本
        for i in range(10):
            # 存储的是值 iamge_batch【i】是tensor类型的对象
            image = image_batch[i].eval().tostring()
            # tensor 类型的shape=(10,)
            label = label_batch[i].eval()[0]
            # 构建样本
            example = tf.train.Example(features=tf.train.Features(feature={
                "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            })
            )
            # 3.将存储的样本序列化
            # 4.进行存储
            writer.write(example.SerializeToString())

            # 关闭写入对象
        writer.close()

        return None

    def read_from_tfrecords(self):
        '''
        从tfrecords格式读取对应的数据
        :return:
        '''
        # 1.根据文件路径创建文件队列
        file_queue = tf.train.string_input_producer(['./data/cifar.train'])
        # 2.创建文件读取对象
        reader = tf.TFRecordReader()

        # 3.进行文件读取
        key,value = reader.read(file_queue)

        # 如何解析value 解析需要按照tfrecord协议版块来
        feature = tf.parse_single_example(value, feature={
            'image': tf.FixedLenFeature([], tf.string),
            'label': tf.FixedLenFeature([], tf.int64)
        })

        # 4.做一些形状处理
        image = tf.decode_raw(feature['image'],tf.uint8)
        label = feature['label']
        # 5.完成批处理的操作 图像形状没有固定，不能够进行批处理操作
        # 修改图片形状
        image_reshape = tf.reshape(image,shape=[self.height,self.width,self.channels])

        image_batch,label_batch = tf.train.batch([image_reshape,label],batch_size=10,num_threads=2,capacity=10)

        return image_batch,label_batch

def call_cifar():
    # 获取某一个路径下的文件名
    file_names = os.listdir('./data/cifar-10-batches-bin/')
    file_names = [os.path.join('./data/cifar-10-batches-bin/', file_name) for file_name in file_names if
                  file_name[-3:] == 'bin']
    # 创建对象 调用对象方法
    cifar = Cifar()
    batch_iamge,batch_label = cifar.read_and_decode(file_names)
    print(batch_iamge,batch_label)

    # 运行已经设定好的图
    with tf.Session() as sess:
        # 开启子线程执行
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess,coord=coord)
        # 将batch_image和batch_label 存储成tfrecords类型的文件
        # print("开始存储")
        # cifar.save_to_tfrecords(batch_image,batch_label)
        # print('存储结束')
        # sess.run([batch_image, batch_label])
        ret = sess.run([batch_iamge,batch_label])
        print(ret)
        coord.request_stop()
        coord.join(threads)



if __name__ == '__main__':
    # fifoqueue()
    # async_opration()
    # csv_reader()
    # pic_reader()
    call_cifar()